package org.baicaixiaozhan.learncommon.util;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * DESC: Redisson Lock 工具集
 *
 * @author baicaixiaozhan
 * @since 2021-07-03
 */
@Slf4j
@AllArgsConstructor
@Component
public class RedissonLockUtils {
    /**
     * 默认等待时间
     */
    private static final int WAIT_TIME = 30;
    /**
     * 加锁名称前缀
     */
    private static final String LOCK_PREFIX = "custom_lock_";

    private final RedissonClient redissonClient;


    private String compareAndAppend(String lockKey) {
        if (lockKey.startsWith(LOCK_PREFIX)) {
            return lockKey;
        }
        return LOCK_PREFIX + lockKey;
    }

    /**
     * 加锁
     *
     * @param lockKey
     * @return {@link RLock}
     */
    public RLock lock(String lockKey) {
        RLock lock = redissonClient.getLock(compareAndAppend(lockKey));
        lock.lock();
        return lock;
    }

    /**
     * 释放锁
     *
     * @param lockKey
     */
    public void unlock(String lockKey) {
        try {
            lockKey = compareAndAppend(lockKey);
            RLock lock = redissonClient.getLock(lockKey);
            if (lock != null && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        } catch (Throwable e) {
            throw new IllegalStateException(String.format("UNLOCK FAILED: key=%s", lockKey), e);
        }
    }

    /**
     * 释放锁
     *
     * @param lock
     */
    public void unlock(RLock lock) {
        lock.unlock();
    }

    /**
     * 带超时的 lock
     *
     * @param lockKey
     * @param timeout 超时时间   单位：秒
     */
    public RLock lock(String lockKey, long timeout) {
        return lock(lockKey, timeout, TimeUnit.SECONDS);
    }

    /**
     * 带超时的 lock
     *
     * @param lockKey
     * @param timeout 超时时间
     * @param unit    时间单位
     */
    public RLock lock(String lockKey, long timeout, TimeUnit unit) {
        RLock lock = redissonClient.getLock(compareAndAppend(lockKey));
        lock.lock(timeout, unit);
        return lock;
    }

    /**
     * 尝试获取锁
     *
     * @param lockKey
     * @param waitTime  最多等待时间
     * @param leaseTime 上锁后自动释放锁时间
     * @return
     */
    public Boolean tryLock(String lockKey, long waitTime, long leaseTime) {
        return tryLock(lockKey, waitTime, leaseTime, TimeUnit.SECONDS);
    }

    /**
     * 尝试获取锁
     *
     * @param lockKey
     * @param waitTime  最多等待时间
     * @param leaseTime 上锁后自动释放锁时间
     * @param unit      时间单位
     * @return
     */
    public Boolean tryLock(String lockKey, long waitTime, long leaseTime, TimeUnit unit) {
        RLock lock = redissonClient.getLock(compareAndAppend(lockKey));
        try {
            return lock.tryLock(waitTime, leaseTime, unit);
        } catch (InterruptedException e) {
            if (log.isWarnEnabled()) {
                log.warn("加锁失败 {}", e.getMessage());
            }
            return false;
        }
    }


    private RLock getLock(String lockKey, boolean enableWriteLock) {
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(compareAndAppend(lockKey));
        return enableWriteLock ? readWriteLock.writeLock() : readWriteLock.readLock();
    }

    /**
     * 读锁
     *
     * @param key 锁的Key
     * @return 加锁结果
     */
    private boolean readLock(String key) {
        try {
            RLock readLock = getLock(key, false);
            return readLock.tryLock(WAIT_TIME, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            if (log.isWarnEnabled()) {
                log.warn("key: {}, 读加锁异常", key, e);
            }
            return false;
        }
    }

    /**
     * 写锁
     *
     * @param key 锁的Key
     * @return 加锁结果
     */
    private boolean writeLock(String key) {
        try {
            RLock writeLock = getLock(key, true);
            return writeLock.tryLock(WAIT_TIME, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            if (log.isWarnEnabled()) {
                log.warn("key: {}, 写加锁异常", key, e);
            }
            return false;
        }
    }

    /**
     * 释放锁
     *
     * @param key             锁的Key
     * @param enableWriteLock true: 写锁, false: 读锁
     */
    private void unlock(String key, boolean enableWriteLock) {
        RLock lock = getLock(key, enableWriteLock);
        if (lock != null && lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }

}
